#pragma once
#include "JsonParser.hpp"
#include <array>
#include <vector>
#include <list>
#include <set>
#include <map>
#include <bitset>

#pragma mark - string

template <>
class JsonParser<std::string>
{
public:
    bool empty(const std::string& value) const {return value.empty();}
    bool read(const rapidjson::Value& doc, std::string& value) {
        if (doc.IsString()) {
            value = doc.GetString();
            return true;
        }
        return false;
    }
    bool write(rapidjson::Document::AllocatorType& allocator, rapidjson::Value& doc, const std::string& value) const {
        if (doc.IsArray()) {
            rapidjson::Value temp;
            write(allocator, temp, value);
            doc.PushBack(temp, allocator);
        } else {
            doc.SetString(value.c_str(), (unsigned)value.size(), allocator);
        }
        return true;
    }

    bool from_key(const std::string& key, std::string& value) const {
        value = key;
        return true;
    }
    bool to_key(const std::string& value, std::string& key) const {
        key = value;
        return true;
    }
};

template <size_t S>
class JsonParser<std::bitset<S>>
{
public:
    bool empty(const std::bitset<S>& value) const {return value.size() == 0;}
    bool read(const rapidjson::Value& doc, std::bitset<S>& value) {
        if (doc.IsString()) {
            value = std::bitset<S>(doc.GetString());
        } else if (doc.IsUint()) {
            value = std::bitset<S>(doc.GetUint());
        } else if (doc.IsUint64()) {
            value = std::bitset<S>(doc.GetUint64());
        }
        return false;
    }
    bool write(rapidjson::Document::AllocatorType& allocator, rapidjson::Value& doc, const std::bitset<S>& value) const {
        return false;
    }
};

template <typename K, typename V>
class JsonParser<std::pair<K,V>>
{
public:
    bool empty(const std::pair<K,V>& value) const {return JsonParser<K>().empty(value.first) && JsonParser<V>().empty(value.second);}
    bool read(const rapidjson::Value& doc, std::pair<K, V>& value) {
        if (doc.IsObject()) {
            if (doc.MemberCount() == 0) {

            }
            else if (doc.MemberCount() == 1) {
                auto it = doc.MemberBegin();
                return JsonParser<K>().read(it->name, value.first) && JsonParser<V>().read(it->value, value.second);
            }
            else {
                return JsonParser<K>().read(doc, "first", value.first) && JsonParser<V>().read(doc, "second", value.second);
            }
        }
        return false;
    }
    bool write(rapidjson::Document::AllocatorType& allocator, rapidjson::Value& doc, const std::pair<K, V>& value) const {
        doc.SetObject();
        return JsonParser<K>().write(allocator, doc, "first", value.first) && JsonParser<V>().write(allocator, doc, "second", value.second);
    }
};

template <typename E, size_t N>
class JsonParser<std::array<E, N>>
{
public:
    bool empty(const std::array<E, N>& value) const {
        if (!value.empty()) {
            auto p = JsonParser<E>();
            for (auto& e : value) {
                if (!p.empty(e)) {
                    return false;
                }
            }
        }
        return true;
    }

    bool write(rapidjson::Document::AllocatorType& allocator, rapidjson::Value& doc, const std::array<E, N>& value) const {
        doc.SetArray();
        auto parser = JsonParser<E>();
        for (auto& e : value) {
            rapidjson::Value temp;
            parser.write(temp, allocator, e);
            doc.PushBack(temp, allocator);
        }
        return true;
    }
};

template <typename E>
class JsonParser<std::set<E>>
{
public:
    bool empty(const std::set<E>& value) const {return value.empty();}

    bool write(rapidjson::Document::AllocatorType& allocator, rapidjson::Value& doc, const std::set<E>& value) const {
        doc.SetArray();
        auto parser = JsonParser<E>();
        for (auto& e : value) {
            rapidjson::Value temp;
            parser.write(temp, allocator, e);
            doc.PushBack(temp, allocator);
        }
        return true;
    }
};

template <typename E>
class JsonParser<std::list<E>>
{
public:
    bool empty(const std::list<E>& value) const {return value.empty();}

    bool write(rapidjson::Document::AllocatorType& allocator, rapidjson::Value& doc, const std::list<E>& value) const {
        doc.SetArray();
        auto parser = JsonParser<E>();
        for (auto& e : value) {
            rapidjson::Value temp;
            parser.write(temp, allocator, e);
            doc.PushBack(temp, allocator);
        }
        return true;
    }
};

template <typename E>
class JsonParser<std::vector<E>>
{
public:
    bool empty(const std::vector<E>& value) const {return value.empty();}

    bool write(rapidjson::Document::AllocatorType& allocator, rapidjson::Value& doc, const std::vector<E>& value) const {
        doc.SetArray();
        auto parser = JsonParser<E>();
        for (auto& e : value) {
            rapidjson::Value temp;
            parser.write(temp, allocator, e);
            doc.PushBack(temp, allocator);
        }
        return true;
    }
};

template <typename K, typename V>
class JsonParser<std::map<K, V>>
{
public:
    bool empty(const std::map<K, V>& value) const {return value.empty();}

    bool write(rapidjson::Document::AllocatorType& allocator, rapidjson::Value& doc, const std::map<K, V>& value) const {
        doc.SetObject();
        auto parserK = JsonParser<K>();
        auto parserV = JsonParser<V>();
        for (auto& pair : value) {
            if (parserV.empty(pair.second)) {
                continue;
            }
            if (!write(allocator, doc, parserK.to_key(pair.first), pair.second)) {
                return false;
            }
        }
        return true;
    }
};
